﻿using Grpc.Core;
using Grpc.Net.Client.Balancer;
using Grpc.Net.Client.Configuration;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Nacos.AspNetCore.V2;
using Nacos.V2;
using System.Net;
using System.Runtime.InteropServices;
using XYS.Core.Models;
using XYS.Core.Util;
using XYS.Core.Util.Helper;
using XYS.Service.Core.Models._Setting;
using XYS.Service.Core.Startup.gRPC;

namespace XYS.Service.Core;

/// <summary>
/// grpc
/// </summary>
public static class GRpcExtension
{

    /// <summary>
    /// add gRpc
    /// </summary>
    /// <param name="builder"></param>
    /// <param name="logger"></param>
    /// <returns></returns>
    public static WebApplicationBuilder AddGRpc(this WebApplicationBuilder builder, ILogger logger)
    {
        //注册服务端 - 是否有grpc 服务的实现, 如果有服务的实现则需要添加Grpc
        if (AssemblyHelper.GetAssemblyTypes().Any(e => e?.BaseType?.CustomAttributes?.FirstOrDefault()?.AttributeType == typeof(Grpc.Core.BindServiceMethodAttribute)))
        {
            var nacosSetting = builder.Configuration.GetSection("nacos").Get<NacosAspNetOptions>();
            if (nacosSetting != null && int.TryParse(GlobalCoreSetting.NACOS_PORT_GRPC, out var gRpcPort))
            {
                string nacosIp = builder.Configuration["NACOS_IP"];
                if (nacosIp.IsNullOrWhiteSpace())
                    nacosIp = nacosSetting.Ip;

                string nacosWeightString = builder.Configuration["NACOS_WEIGHT_GRPC"];
                if (!double.TryParse(nacosWeightString, out var nacosWeight))
                {
                    nacosWeightString = builder.Configuration["NACOS_WEIGHT"];
                    if (!double.TryParse(nacosWeightString, out nacosWeight))
                        nacosWeight = nacosSetting.Weight;
                }
                if (nacosWeight > 0)
                {
                    // 获取 Nacos 客户端实例
                    var nacosNamingService = builder.Services.BuildServiceProvider().GetRequiredService<INacosNamingService>();
                    // 注册 gRPC 实例
                    var httpInstance = new Nacos.V2.Naming.Dtos.Instance
                    {
                        Ip = nacosIp,
                        Port = gRpcPort,
                        Weight = nacosWeight,
                        Metadata = new Dictionary<string, string>
                        {
                            [NacosMetadataKeys.Protocol] = NacosMetadataKeys.Grpc,
                            [NacosMetadataKeys.DotnetVersion] = RuntimeInformation.FrameworkDescription
                        }
                    };
                    // 注册 gRPC 实例
                    nacosNamingService.RegisterInstance(nacosSetting.ServiceName, httpInstance);
                }
            }
            builder.Services.AddGrpc();
            logger.LogInformation("添加GRpc服务端支持");
        }


        //注册客户端
        var gRpcSettings = builder.Configuration.GetSection("GRpcSettings").Get<IEnumerable<GRpcSetting>>();
        if (gRpcSettings != null && gRpcSettings.Any())
        {
            builder.Services.AddSingleton<INacosNamingService, Nacos.V2.Naming.NacosNamingService>();

            // 注册自定义的 NacosResolverFactory
            builder.Services.AddSingleton<ResolverFactory>(provider =>
                new NacosResolverFactory(provider.GetRequiredService<INacosNamingService>()));

            HttpClient.DefaultProxy = new WebProxy();
            foreach (var gRpcSetting in gRpcSettings)
            {
                if (gRpcSetting.GRpcServices != null && gRpcSetting.GRpcServices.Count > 0)
                {
                    HttpClientHandler httpHandler = new() { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator };

                    string channelAddress = gRpcSetting.Host.Count(x => x == ':') > 1 ? gRpcSetting.Host : $"{gRpcSetting.Host}{(gRpcSetting.Port == null ? "" : $":{gRpcSetting.Port}")}";

                    foreach (var serviceName in gRpcSetting.GRpcServices)
                    {
                        var serviceType = AssemblyHelper.GetAssemblyTypes()
                            .FirstOrDefault(e => typeof(Grpc.Core.ClientBase).IsAssignableFrom(e)
                            && e.Name == $"{serviceName}Client");
                        if (serviceType != null)
                        {
                            var methodAddGrpcClients = typeof(GrpcClientServiceExtensions).GetMethods().Where(e => e.Name == "AddGrpcClient").ToList();

                            var methodAddGrpcClient = methodAddGrpcClients[1];
                            Action<Grpc.Net.ClientFactory.GrpcClientFactoryOptions> configureClient = options => options.Address = new Uri(channelAddress);
                            var res = methodAddGrpcClient.MakeGenericMethod(serviceType).Invoke(builder.Services, new object[] { builder.Services, configureClient });

                            if (gRpcSetting.Host.StartsWith("nacos") && res is IHttpClientBuilder httpclientbuilder)
                            {
                                httpclientbuilder.ConfigureChannel(channel =>
                                 {
                                     channel.Credentials = ChannelCredentials.Insecure;
                                     channel.ServiceConfig = new ServiceConfig
                                     {
                                         LoadBalancingConfigs = { new RoundRobinConfig() } // 负载均衡策略
                                     };
                                 });
                            }

                            logger.LogInformation(message: "注册grpc客户端[{channelAddress}][{serviceName}Client]", channelAddress, serviceName);
                        }
                    }

                }
            }
        }

        return builder;
    }

    /// <summary>
    /// use gRpc
    /// </summary>
    /// <param name="app"></param>
    /// <param name="logger"></param>
    /// <returns></returns>
    public static WebApplication UseGRpc(this WebApplication app, ILogger logger)
    {
        //自动注入gRcpServices
        var serviceImpls = AssemblyHelper.GetAssemblyTypes()
            .Where(e => e.BaseType != null
                && !string.IsNullOrWhiteSpace(e.BaseType.FullName)
                && e.BaseType.FullName.StartsWith("GrpcService."))
            .ToList();

        foreach (var serviceImpl in serviceImpls)
        {
            var grpcEndpointRouteBuilderExtensionsType = typeof(Microsoft.AspNetCore.Builder.GrpcEndpointRouteBuilderExtensions);
            var methodInfoMapGrpcService = grpcEndpointRouteBuilderExtensionsType.GetMethods().FirstOrDefault(e => e.Name == "MapGrpcService");

            methodInfoMapGrpcService?.MakeGenericMethod(serviceImpl).Invoke(app, new object[] { app });

            logger?.LogInformation($"注册grpc实现【{serviceImpl.FullName}】");
        }

        return app;
    }
}
